來到最後一天了!以我最感興趣的反向工程作為結尾!
題目希望我們破解本地的 binary file ,並以這組 password 連上 server,並且提示告訴我們使用機器去解決符號。
hint 1:Let the machine figure out the symbols!
下載題目給的 elf 檔案,並且試著連上 webshell,會發現要輸入密碼。
$ ls -l
-rw-rw-r-- 1 peggggy-picoctf peggggy-picoctf 19112 Mar 12 00:36 crackme100
$ nc titan.picoctf.net 51974
Enter the secret password:
1
FAILED!
題目的提示告訴我們,讓機器去解決符號,於是我上網 google,發現方法不外乎是使用:
(1) IDA ( 靜態分析 )、(2) GDB ( 動態分析 )、(3) 自動分析 ( 詳情可參閱:Binary 自動分析的那些事 )
靜態分析重在程式結構和潛在問題的靜態檢查,代表工具如 IDA。動態分析重在程式運行時的行為監控和問題發現,代表工具如 GDB。自動分析結合上述兩種技術,利用自動化工具進行程式分析。
這裡我想採用自動分析,於是我又去查了 angr
這個套件怎麼使用 ( 參閱:Simulation Managers 、angr初探 ),然後知道要使用 simgr.explore( find , avoid )
,所以要知道想要跳到 ( find ) 的地址 ,以及想要避免 ( avoid ) 的地址。
因此,使用 Ghidra
( 詳情請見: 必備!好用的逆向分析工具Ghidra入門介紹 ) 分析得到的 elf 檔案,並且可以看到 “Success!........”
在 0x00401378 的位置,”FAILED!”
在 0x00401389 的位置。
於是我們可以寫出以下的檔案,並且執行。執行後會得到一串 binary 的字串 ( 只是要跑有點久 )。
#!/usr/bin/env python3
import angr
def main():
proj = angr.Project("crackme100")
state = proj.factory.entry_state()
simgr = proj.factory.simgr(state)
simgr.explore(find=0x00401378, avoid=0x00401389)
return simgr.found[0].posix.dumps(0)
if __name__ == '__main__':
print(main())
$ python3 script.py
WARNING | 2024-08-05 00:19:50,922 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory with an unspecified value. This could indicate unwanted behavior.
WARNING | 2024-08-05 00:19:50,923 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2024-08-05 00:19:50,923 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2024-08-05 00:19:50,923 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2024-08-05 00:19:50,923 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2024-08-05 00:19:50,923 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff8c with 4 unconstrained bytes referenced from 0x401095 (_start+0x5 in crackme100 (0x401095))
WARNING | 2024-08-05 00:19:51,221 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff33 with 41 unconstrained bytes referenced from 0x59dc90 (strlen+0x0 in libc.so.6 (0x9dc90))
WARNING | 2024-08-05 00:19:51,221 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff70 with 8 unconstrained bytes referenced from 0x59dc90 (strlen+0x0 in libc.so.6 (0x9dc90))
b'zqncqnqkun}\\s)igianqoofjf]ub\xd7fgyilppb_j\xbcroi\xf2fl|A\\}'
再使用 pwntool,連上 webshell,並且傳送剛剛所得到字串。
#!/usr/bin/env python3
from pwn import *
context.log_level='critical'
p = remote("titan.picoctf.net", 51974)
p.recvuntil(b"Enter the secret password: ")
p.sendline(b"zqncqnqkun}\\s)igianqoofjf]ub\xd7fgyilppb_j\xbcroi\xf2fl|A\\}")
print(p.recvall())
接著就可以得到 flag 了!
$ python3 pwnscript.py
b'SUCCESS! Here is your flag: picoCTF{s0lv3_angry_symb0ls_4656b68e}\n'
當然,若是不想使用 angr
自動分析,也可以使用 Ghidra
得到 decompile 的 code ( 以下是 decompile 後完整的 code ),再人工轉換成 python script,或是使用 z3 ( 詳閱:Z3 API in Python,但這個我不太會寫就不提供 code 了)。
undefined8 main(void)
{
int iVar1;
size_t sVar2;
char local_a8 [64];
undefined8 local_68;
undefined8 local_60;
undefined8 local_58;
undefined8 local_50;
undefined8 local_48;
undefined7 local_40;
undefined4 uStack57;
uint local_2c;
uint local_28;
char local_21;
uint local_20;
uint local_1c;
uint local_18;
int local_14;
int local_10;
int local_c;
local_68 = 0x747774746971747a;
local_60 = 0x7372667965697478;
local_58 = 0x766f78757a74676c;
local_50 = 0x6e7372626e64666c;
local_48 = 0x647368687976726c;
local_40 = 0x6e786f66727878;
uStack57 = 0x6c626a;
setvbuf(stdout,(char *)0x0,2,0);
printf("Enter the secret password: ");
__isoc99_scanf(&DAT_00402024);
local_c = 0;
sVar2 = strlen((char *)&local_68);
local_14 = (int)sVar2;
local_18 = 0x55;
local_1c = 0x33;
local_20 = 0xf;
local_21 = 'a';
for (; local_c < 3; local_c = local_c + 1) {
for (local_10 = 0; local_10 < local_14; local_10 = local_10 + 1) {
local_28 = (local_10 % 0xff >> 1 & local_18) + (local_10 % 0xff & local_18);
local_2c = ((int)local_28 >> 2 & local_1c) + (local_1c & local_28);
iVar1 = ((int)local_2c >> 4 & local_20) +
((int)local_a8[local_10] - (int)local_21) + (local_20 & local_2c);
local_a8[local_10] = local_21 + (char)iVar1 + (char)(iVar1 / 0x1a) * -0x1a;
}
}
iVar1 = memcmp(local_a8,&local_68,(long)local_14);
if (iVar1 == 0) {
printf("SUCCESS! Here is your flag: %s\n","picoCTF{sample_flag}");
}
else {
puts("FAILED!");
}
return 0;
}
小結:
學會 angr
自動分析,以及使用 Ghidra
。
終於完賽啦!